//===--- FrontendOptions.cpp ----------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//

#include "polarphp/frontend/FrontendOptions.h"

#include "polarphp/ast/DiagnosticsFrontend.h"
#include "polarphp/option/Options.h"
#include "polarphp/llparser/Lexer.h"
#include "polarphp/global/NameStrings.h"

#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Option/Option.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/LineIterator.h"
#include "llvm/Support/Path.h"

using namespace polar;
using namespace llvm::opt;

bool FrontendOptions::needsProperModuleName(ActionType action) {
   switch (action) {
      case ActionType::NoneAction:
      case ActionType::Parse:
      case ActionType::ResolveImports:
      case ActionType::Typecheck:
      case ActionType::DumpParse:
      case ActionType::DumpAST:
      case ActionType::EmitSyntax:
      case ActionType::DumpInterfaceHash:
      case ActionType::PrintAST:
      case ActionType::DumpScopeMaps:
      case ActionType::DumpTypeRefinementContexts:
      case ActionType::DumpPCM:
         return false;
      case ActionType::EmitPCH:
      case ActionType::EmitPILGen:
      case ActionType::EmitPIL:
      case ActionType::EmitPIBGen:
      case ActionType::EmitPIB:
      case ActionType::EmitModuleOnly:
      case ActionType::MergeModules:
      case ActionType::CompileModuleFromInterface:
         return true;
      case ActionType::Immediate:
      case ActionType::REPL:
         return false;
      case ActionType::EmitAssembly:
      case ActionType::EmitIR:
      case ActionType::EmitBC:
      case ActionType::EmitObject:
      case ActionType::EmitImportedModules:
      case ActionType::DumpTypeInfo:
      case ActionType::EmitPCM:
         return true;
   }
   llvm_unreachable("Unknown ActionType");
}

bool FrontendOptions::isActionImmediate(ActionType action) {
   switch (action) {
      case ActionType::NoneAction:
      case ActionType::Parse:
      case ActionType::ResolveImports:
      case ActionType::Typecheck:
      case ActionType::DumpParse:
      case ActionType::DumpAST:
      case ActionType::EmitSyntax:
      case ActionType::DumpInterfaceHash:
      case ActionType::PrintAST:
      case ActionType::DumpScopeMaps:
      case ActionType::DumpTypeRefinementContexts:
      case ActionType::EmitPCH:
      case ActionType::EmitPILGen:
      case ActionType::EmitPIL:
      case ActionType::EmitPIBGen:
      case ActionType::EmitPIB:
      case ActionType::EmitModuleOnly:
      case ActionType::MergeModules:
      case ActionType::CompileModuleFromInterface:
         return false;
      case ActionType::Immediate:
      case ActionType::REPL:
         return true;
      case ActionType::EmitAssembly:
      case ActionType::EmitIR:
      case ActionType::EmitBC:
      case ActionType::EmitObject:
      case ActionType::EmitImportedModules:
      case ActionType::DumpTypeInfo:
      case ActionType::EmitPCM:
      case ActionType::DumpPCM:
         return false;
   }
   llvm_unreachable("Unknown ActionType");
}

bool FrontendOptions::shouldActionOnlyParse(ActionType action) {
   switch (action) {
      case FrontendOptions::ActionType::Parse:
      case FrontendOptions::ActionType::DumpParse:
      case FrontendOptions::ActionType::EmitSyntax:
      case FrontendOptions::ActionType::DumpInterfaceHash:
      case FrontendOptions::ActionType::EmitImportedModules:
         return true;
      default:
         return false;
   }
}

void FrontendOptions::forAllOutputPaths(
   const InputFile &input, llvm::function_ref<void(StringRef)> fn) const {
   if (RequestedAction != FrontendOptions::ActionType::EmitModuleOnly &&
       RequestedAction != FrontendOptions::ActionType::MergeModules) {
      if (InputsAndOutputs.isWholeModule())
         InputsAndOutputs.forEachOutputFilename(fn);
      else
         fn(input.outputFilename());
   }
   const SupplementaryOutputPaths &outs =
      input.getPrimarySpecificPaths().supplementaryOutputs;
   const std::string *outputs[] = {&outs.ModuleOutputPath,
                                   &outs.ModuleDocOutputPath,
                                   &outs.ModuleInterfaceOutputPath,
//                                   &outs.ObjCHeaderOutputPath,
                                   &outs.ModuleSourceInfoOutputPath};
   for (const std::string *next : outputs) {
      if (!next->empty())
         fn(*next);
   }
}

filetypes::FileTypeId
FrontendOptions::formatForPrincipalOutputFileForAction(ActionType action) {
   using namespace filetypes;

   switch (action) {
      case ActionType::NoneAction:
         return TY_Nothing;

      case ActionType::Parse:
      case ActionType::ResolveImports:
      case ActionType::Typecheck:
      case ActionType::DumpParse:
      case ActionType::DumpInterfaceHash:
      case ActionType::DumpAST:
      case ActionType::EmitSyntax:
      case ActionType::PrintAST:
      case ActionType::DumpScopeMaps:
      case ActionType::DumpTypeRefinementContexts:
      case ActionType::DumpTypeInfo:
      case ActionType::DumpPCM:
         return TY_Nothing;

      case ActionType::EmitPCH:
         return TY_PCH;

      case ActionType::EmitPILGen:
         return TY_RawPIL;

      case ActionType::EmitPIL:
         return TY_PIL;

      case ActionType::EmitPIBGen:
         return TY_RawPIB;

      case ActionType::EmitPIB:
         return TY_PIB;

      case ActionType::MergeModules:
      case ActionType::EmitModuleOnly:
      case ActionType::CompileModuleFromInterface:
         return TY_PHPModuleFile;

      case ActionType::Immediate:
      case ActionType::REPL:
         // These modes have no frontend-generated output.
         return TY_Nothing;

      case ActionType::EmitAssembly:
         return TY_Assembly;

      case ActionType::EmitIR:
         return TY_LLVM_IR;

      case ActionType::EmitBC:
         return TY_LLVM_BC;

      case ActionType::EmitObject:
         return TY_Object;

      case ActionType::EmitImportedModules:
         return TY_ImportedModules;

      case ActionType::EmitPCM:
         return TY_ClangModuleFile;
   }
   llvm_unreachable("unhandled action");
}

bool FrontendOptions::canActionEmitDependencies(ActionType action) {
   switch (action) {
      case ActionType::NoneAction:
      case ActionType::Parse:
      case ActionType::DumpParse:
      case ActionType::DumpInterfaceHash:
      case ActionType::DumpAST:
      case ActionType::EmitSyntax:
      case ActionType::PrintAST:
      case ActionType::DumpScopeMaps:
      case ActionType::DumpTypeRefinementContexts:
      case ActionType::DumpTypeInfo:
      case ActionType::CompileModuleFromInterface:
      case ActionType::Immediate:
      case ActionType::REPL:
      case ActionType::DumpPCM:
         return false;
      case ActionType::ResolveImports:
      case ActionType::Typecheck:
      case ActionType::MergeModules:
      case ActionType::EmitModuleOnly:
      case ActionType::EmitPCH:
      case ActionType::EmitPILGen:
      case ActionType::EmitPIL:
      case ActionType::EmitPIBGen:
      case ActionType::EmitPIB:
      case ActionType::EmitIR:
      case ActionType::EmitBC:
      case ActionType::EmitAssembly:
      case ActionType::EmitObject:
      case ActionType::EmitImportedModules:
      case ActionType::EmitPCM:
         return true;
   }
   llvm_unreachable("unhandled action");
}

bool FrontendOptions::canActionEmitReferenceDependencies(ActionType action) {
   switch (action) {
      case ActionType::NoneAction:
      case ActionType::Parse:
      case ActionType::ResolveImports:
      case ActionType::DumpParse:
      case ActionType::DumpInterfaceHash:
      case ActionType::DumpAST:
      case ActionType::EmitSyntax:
      case ActionType::PrintAST:
      case ActionType::DumpScopeMaps:
      case ActionType::DumpTypeRefinementContexts:
      case ActionType::DumpTypeInfo:
      case ActionType::CompileModuleFromInterface:
      case ActionType::Immediate:
      case ActionType::REPL:
      case ActionType::EmitPCM:
      case ActionType::DumpPCM:
         return false;
      case ActionType::Typecheck:
      case ActionType::MergeModules:
      case ActionType::EmitModuleOnly:
      case ActionType::EmitPCH:
      case ActionType::EmitPILGen:
      case ActionType::EmitPIL:
      case ActionType::EmitPIBGen:
      case ActionType::EmitPIB:
      case ActionType::EmitIR:
      case ActionType::EmitBC:
      case ActionType::EmitAssembly:
      case ActionType::EmitObject:
      case ActionType::EmitImportedModules:
         return true;
   }
   llvm_unreachable("unhandled action");
}

bool FrontendOptions::canActionEmitPHPRanges(ActionType action) {
   return canActionEmitReferenceDependencies(action);
}

bool FrontendOptions::canActionEmitCompiledSource(ActionType action) {
   return canActionEmitReferenceDependencies(action);
}

bool FrontendOptions::canActionEmitObjCHeader(ActionType action) {
   switch (action) {
      case ActionType::NoneAction:
      case ActionType::Parse:
      case ActionType::ResolveImports:
      case ActionType::DumpParse:
      case ActionType::DumpInterfaceHash:
      case ActionType::DumpAST:
      case ActionType::EmitSyntax:
      case ActionType::PrintAST:
      case ActionType::EmitPCH:
      case ActionType::DumpScopeMaps:
      case ActionType::DumpTypeRefinementContexts:
      case ActionType::DumpTypeInfo:
      case ActionType::CompileModuleFromInterface:
      case ActionType::Immediate:
      case ActionType::REPL:
      case ActionType::EmitPCM:
      case ActionType::DumpPCM:
         return false;
      case ActionType::Typecheck:
      case ActionType::MergeModules:
      case ActionType::EmitModuleOnly:
      case ActionType::EmitPILGen:
      case ActionType::EmitPIL:
      case ActionType::EmitPIBGen:
      case ActionType::EmitPIB:
      case ActionType::EmitIR:
      case ActionType::EmitBC:
      case ActionType::EmitAssembly:
      case ActionType::EmitObject:
      case ActionType::EmitImportedModules:
         return true;
   }
   llvm_unreachable("unhandled action");
}

bool FrontendOptions::canActionEmitLoadedModuleTrace(ActionType action) {
   switch (action) {
      case ActionType::NoneAction:
      case ActionType::Parse:
      case ActionType::DumpParse:
      case ActionType::DumpInterfaceHash:
      case ActionType::DumpAST:
      case ActionType::EmitSyntax:
      case ActionType::PrintAST:
      case ActionType::DumpScopeMaps:
      case ActionType::DumpTypeRefinementContexts:
      case ActionType::DumpTypeInfo:
      case ActionType::CompileModuleFromInterface:
      case ActionType::Immediate:
      case ActionType::REPL:
      case ActionType::EmitPCM:
      case ActionType::DumpPCM:
         return false;
      case ActionType::ResolveImports:
      case ActionType::Typecheck:
      case ActionType::MergeModules:
      case ActionType::EmitModuleOnly:
      case ActionType::EmitPCH:
      case ActionType::EmitPILGen:
      case ActionType::EmitPIL:
      case ActionType::EmitPIBGen:
      case ActionType::EmitPIB:
      case ActionType::EmitIR:
      case ActionType::EmitBC:
      case ActionType::EmitAssembly:
      case ActionType::EmitObject:
      case ActionType::EmitImportedModules:
         return true;
   }
   llvm_unreachable("unhandled action");
}

bool FrontendOptions::canActionEmitModule(ActionType action) {
   switch (action) {
      case ActionType::NoneAction:
      case ActionType::Parse:
      case ActionType::ResolveImports:
      case ActionType::Typecheck:
      case ActionType::DumpParse:
      case ActionType::DumpInterfaceHash:
      case ActionType::DumpAST:
      case ActionType::EmitSyntax:
      case ActionType::PrintAST:
      case ActionType::EmitPCH:
      case ActionType::DumpScopeMaps:
      case ActionType::DumpTypeRefinementContexts:
      case ActionType::DumpTypeInfo:
      case ActionType::EmitPILGen:
      case ActionType::CompileModuleFromInterface:
      case ActionType::Immediate:
      case ActionType::REPL:
      case ActionType::EmitPCM:
      case ActionType::DumpPCM:
         return false;
      case ActionType::MergeModules:
      case ActionType::EmitModuleOnly:
      case ActionType::EmitPIL:
      case ActionType::EmitPIBGen:
      case ActionType::EmitPIB:
      case ActionType::EmitIR:
      case ActionType::EmitBC:
      case ActionType::EmitAssembly:
      case ActionType::EmitObject:
      case ActionType::EmitImportedModules:
         return true;
   }
   llvm_unreachable("unhandled action");
}

bool FrontendOptions::canActionEmitModuleDoc(ActionType action) {
   return canActionEmitModule(action);
}

bool FrontendOptions::canActionEmitInterface(ActionType action) {
   switch (action) {
      case ActionType::NoneAction:
      case ActionType::Parse:
      case ActionType::ResolveImports:
      case ActionType::DumpParse:
      case ActionType::DumpInterfaceHash:
      case ActionType::DumpAST:
      case ActionType::EmitSyntax:
      case ActionType::PrintAST:
      case ActionType::EmitImportedModules:
      case ActionType::EmitPCH:
      case ActionType::DumpScopeMaps:
      case ActionType::DumpTypeRefinementContexts:
      case ActionType::DumpTypeInfo:
      case ActionType::EmitPILGen:
      case ActionType::EmitPIBGen:
      case ActionType::CompileModuleFromInterface:
      case ActionType::Immediate:
      case ActionType::REPL:
      case ActionType::EmitPCM:
      case ActionType::DumpPCM:
         return false;
      case ActionType::Typecheck:
      case ActionType::MergeModules:
      case ActionType::EmitModuleOnly:
      case ActionType::EmitPIL:
      case ActionType::EmitPIB:
      case ActionType::EmitIR:
      case ActionType::EmitBC:
      case ActionType::EmitAssembly:
      case ActionType::EmitObject:
         return true;
   }
   llvm_unreachable("unhandled action");
}

bool FrontendOptions::doesActionProduceOutput(ActionType action) {
   switch (action) {
      case ActionType::Parse:
      case ActionType::ResolveImports:
      case ActionType::Typecheck:
      case ActionType::DumpParse:
      case ActionType::DumpAST:
      case ActionType::EmitSyntax:
      case ActionType::DumpInterfaceHash:
      case ActionType::PrintAST:
      case ActionType::DumpScopeMaps:
      case ActionType::DumpTypeRefinementContexts:
      case ActionType::EmitPCH:
      case ActionType::EmitPILGen:
      case ActionType::EmitPIL:
      case ActionType::EmitPIBGen:
      case ActionType::EmitPIB:
      case ActionType::EmitModuleOnly:
      case ActionType::EmitAssembly:
      case ActionType::EmitIR:
      case ActionType::EmitBC:
      case ActionType::EmitObject:
      case ActionType::EmitImportedModules:
      case ActionType::MergeModules:
      case ActionType::CompileModuleFromInterface:
      case ActionType::DumpTypeInfo:
      case ActionType::EmitPCM:
      case ActionType::DumpPCM:
         return true;

      case ActionType::NoneAction:
      case ActionType::Immediate:
      case ActionType::REPL:
         return false;
   }
   llvm_unreachable("Unknown ActionType");
}

bool FrontendOptions::doesActionProduceTextualOutput(ActionType action) {
   switch (action) {
      case ActionType::NoneAction:
      case ActionType::EmitPCH:
      case ActionType::EmitPIBGen:
      case ActionType::EmitPIB:
      case ActionType::MergeModules:
      case ActionType::EmitModuleOnly:
      case ActionType::CompileModuleFromInterface:
      case ActionType::EmitBC:
      case ActionType::EmitObject:
      case ActionType::Immediate:
      case ActionType::REPL:
      case ActionType::EmitPCM:
         return false;

      case ActionType::Parse:
      case ActionType::ResolveImports:
      case ActionType::Typecheck:
      case ActionType::DumpParse:
      case ActionType::DumpInterfaceHash:
      case ActionType::DumpAST:
      case ActionType::EmitSyntax:
      case ActionType::PrintAST:
      case ActionType::DumpScopeMaps:
      case ActionType::DumpTypeRefinementContexts:
      case ActionType::EmitImportedModules:
      case ActionType::EmitPILGen:
      case ActionType::EmitPIL:
      case ActionType::EmitAssembly:
      case ActionType::EmitIR:
      case ActionType::DumpTypeInfo:
      case ActionType::DumpPCM:
         return true;
   }
   llvm_unreachable("unhandled action");
}

bool FrontendOptions::doesActionGeneratePIL(ActionType action) {
   switch (action) {
      case ActionType::NoneAction:
      case ActionType::Parse:
      case ActionType::ResolveImports:
      case ActionType::Typecheck:
      case ActionType::DumpParse:
      case ActionType::DumpInterfaceHash:
      case ActionType::EmitSyntax:
      case ActionType::DumpAST:
      case ActionType::PrintAST:
      case ActionType::DumpScopeMaps:
      case ActionType::DumpTypeRefinementContexts:
      case ActionType::EmitImportedModules:
      case ActionType::EmitPCH:
      case ActionType::CompileModuleFromInterface:
      case ActionType::EmitPCM:
      case ActionType::DumpPCM:
         return false;
      case ActionType::EmitPILGen:
      case ActionType::EmitPIBGen:
      case ActionType::EmitPIL:
      case ActionType::EmitPIB:
      case ActionType::EmitModuleOnly:
      case ActionType::MergeModules:
      case ActionType::Immediate:
      case ActionType::REPL:
      case ActionType::EmitAssembly:
      case ActionType::EmitIR:
      case ActionType::EmitBC:
      case ActionType::EmitObject:
      case ActionType::DumpTypeInfo:
         return true;
   }
   llvm_unreachable("unhandled action");
}

bool FrontendOptions::doesActionGenerateIR(ActionType action) {
   switch (action) {
      case ActionType::NoneAction:
      case ActionType::Parse:
      case ActionType::DumpParse:
      case ActionType::DumpInterfaceHash:
      case ActionType::DumpAST:
      case ActionType::EmitSyntax:
      case ActionType::PrintAST:
      case ActionType::DumpScopeMaps:
      case ActionType::DumpTypeRefinementContexts:
      case ActionType::DumpTypeInfo:
      case ActionType::CompileModuleFromInterface:
      case ActionType::Typecheck:
      case ActionType::ResolveImports:
      case ActionType::MergeModules:
      case ActionType::EmitModuleOnly:
      case ActionType::EmitPCH:
      case ActionType::EmitPILGen:
      case ActionType::EmitPIL:
      case ActionType::EmitPIBGen:
      case ActionType::EmitPIB:
      case ActionType::EmitImportedModules:
      case ActionType::EmitPCM:
      case ActionType::DumpPCM:
         return false;
      case ActionType::Immediate:
      case ActionType::REPL:
      case ActionType::EmitIR:
      case ActionType::EmitBC:
      case ActionType::EmitAssembly:
      case ActionType::EmitObject:
         return true;
   }
   llvm_unreachable("unhandled action");
}


const PrimarySpecificPaths &
FrontendOptions::getPrimarySpecificPathsForAtMostOnePrimary() const {
   return InputsAndOutputs.getPrimarySpecificPathsForAtMostOnePrimary();
}

const PrimarySpecificPaths &
FrontendOptions::getPrimarySpecificPathsForPrimary(StringRef filename) const {
   return InputsAndOutputs.getPrimarySpecificPathsForPrimary(filename);
}
